#include "avi.h"
#ifdef _PLATFORM_WIN32

#include <cassert>

#include "graphics/graphics.h"


#include "util/timer.h"		    
#include "util/log.h"		    // log
#include "util/common_macros.h"		// macros

#include "vfs/vfs.h"

#include "util/mmgr.h"	

AVI::AVI() : 
	m_DataA(NULL),
	m_ResizeDataA(NULL),
	m_LastFrame(0),
	m_MSPerFrame(0),
	m_CurrentFrame(0),
	m_TimeLeft(0),
	m_Paused(false),
	m_Loaded(false),
    m_Looping(true),
	m_hBitmap(0),
	m_hDrawDIB(0)
{
	m_hDC				= CreateCompatibleDC(0);
	m_Width = 0;
    m_Height = 0;

	m_pAVIStream = 0;
	m_pGetFrame = 0;
	m_hBitmap = 0;
	m_hDrawDIB = 0;
	m_Loaded  = false;
}

AVI::~AVI()
{           
	if (m_hDrawDIB)		DrawDibClose(m_hDrawDIB);								// Closes The DrawDib Device Context
	if (m_pAVIStream)
	{
		DeleteObject(m_hBitmap);								// Delete The Device Dependant Bitmap Object
		AVIStreamGetFrameClose(m_pGetFrame);					// Deallocates The GetFrame Resources
		AVIStreamRelease(m_pAVIStream);							// Release The Stream
		AVIFileExit();											// Release The File
	}
}

void	AVI::Flush()
{
	ShutdownTexture();
}

void	AVI::Recreate()
{
	InitTexture();
}


bool    AVI::Load(const std::string& _File)
{
    _LOG(MSG_WARNING, "Loading AVI file " << _File);
	std::string path = vfs::VFS::Instance().GetPath(_File);
    if (path.empty())
    {
        _LOG(MSG_WARNING, "Unable to find AVI " << _File);
        return false;
    }
    _LOG(MSG_WARNING, "	File Loaded into memory");

	m_hDrawDIB = DrawDibOpen();							        // Grab A Device Context For Our Dib
    _LOG(MSG_WARNING, "	Device Independent Bitmap open");
	AVIFileInit();												// Opens The AVIFile Library
    _LOG(MSG_WARNING, "	AVI files initialized");

    // Opens The AVI Stream
    if (AVIStreamOpenFromFile(&m_pAVIStream, path.c_str(), streamtypeVIDEO, 0, OF_READ, NULL) !=0)
	{
		// An Error Occurred Opening The Stream
        _LOG(MSG_ERROR, "Unable to load Video " << _File);
		return false;
	}
	    _LOG(MSG_WARNING, "	AVI stream open from " << path);

	AVIStreamInfo(m_pAVIStream, &m_AVIStreamInfo, sizeof(m_AVIStreamInfo));				// Reads Information About The Stream Into m_AVIStreamInfo
	m_Width     =   m_AVIStreamInfo.rcFrame.right-m_AVIStreamInfo.rcFrame.left;			// Width Is Right Side Of Frame Minus Left
	m_Height    =   m_AVIStreamInfo.rcFrame.bottom-m_AVIStreamInfo.rcFrame.top;			// Height Is Bottom Of Frame Minus Top
	m_LastFrame =   AVIStreamLength(m_pAVIStream);							            // The Last Frame Of The Stream
	m_MSPerFrame=   AVIStreamSampleToTime(m_pAVIStream,m_LastFrame)/m_LastFrame;		// Calculate Rough Milliseconds Per Frame

	_LOG(MSG_WARNING, "	AVI : [" << m_Width << " x " << m_Height << "] Frames : " << m_LastFrame);


    // Create the bitmap
	m_BMInfo.biSize     = sizeof (BITMAPINFOHEADER);				// Size Of The BitmapInfoHeader
	m_BMInfo.biPlanes   = 1;										// Bitplanes	
	m_BMInfo.biBitCount = 32;										// Bits Format We Want (32 Bit, 4 Bytes)
	m_BMInfo.biWidth    = m_Width;									// Width We Want (from movie)
	m_BMInfo.biHeight   = m_Height;								    // Height We Want (from movie)
	m_BMInfo.biCompression = BI_RGB;								// Requested Mode = RGB
    m_hBitmap = CreateDIBSection (m_hDC, (BITMAPINFO*)(&m_BMInfo), DIB_RGB_COLORS, (void**)(&m_ResizeDataA), NULL, NULL);
    _LOG(MSG_WARNING, "	Bitmap created");

	SelectObject (m_hDC, m_hBitmap);								// Select m_hBitmap Into Our Device Context (m_hDC)
    _LOG(MSG_WARNING, "	Bitmap selected as drawing context");

    // Open the stream
	m_pGetFrame=AVIStreamGetFrameOpen(m_pAVIStream, NULL);						// Create The PGETFRAME	Using Our Request Mode
	if (!m_pGetFrame)
	{
		// An Error Occurred Opening The Frame
        _LOG(MSG_ERROR, "Unable to open frame from " << _File);
        // Shutdown ?
        return false;
	}
    _LOG(MSG_WARNING, "	File stream open. Frame acquired");

    // Setup the default playback
	m_TimeLeft      = m_MSPerFrame/1000.0f;
	m_CurrentFrame  = 0;

    _LOG(MSG_WARNING, "	Attempting to intialize texture");
 	if (!InitTexture())
		return false;
    _LOG(MSG_WARNING, "	Texture successfully initialized");

	// Success
    m_Loaded        = true;

	BlitToTexture();
    _LOG(MSG_WARNING, "	Finished initial blit to set up texture");

    return Image::Load(_File);
}

bool    AVI::Tick()
{
    if (m_Paused) return false; // no need to update
    if (!m_Loaded) return false; // cannot update
	bool		FileEnded = false;

    int AdvanceFrame = 0;
    m_TimeLeft -= TIMER().Delta();
	while (m_TimeLeft<=0.0f)
	{
		m_TimeLeft += m_MSPerFrame/1000.0f;	// Time to next frame
		m_CurrentFrame ++;	// Next frame
        AdvanceFrame ++;
		if (m_CurrentFrame >= m_LastFrame) 
        {
			FileEnded = true;
            if (m_Looping)
            {
			    m_CurrentFrame = 0;
            } else
            {
                m_CurrentFrame = m_LastFrame-1;
            }
        }
	} 
    if (AdvanceFrame <=0)
        return false;    // no need to update raster

	LPBITMAPINFOHEADER lpbi;									// Holds The Bitmap Header Information
	lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(m_pGetFrame, m_CurrentFrame);	// Grab Data From The AVI Stream
    if (!lpbi)
    {
        // We received a bogus message:
        _LOG(MSG_WARNING, "Illegal AVI frame found.. pausing movie");
        m_Paused = true;
        return false;
    }

	m_DataA =(char *)lpbi+lpbi->biSize+lpbi->biClrUsed * sizeof(RGBQUAD);	// Pointer To m_bDataA Returned By AVIStreamGetFrame

	// Convert m_bDataA To Requested Bitmap Format
    // We use this blit to ensure we're getting the default RGB
	if (!DrawDibDraw(m_hDrawDIB, m_hDC, 0, 0, m_Width, m_Height, lpbi, m_DataA, 0, 0, m_Width, m_Height, 0))
	{
        // We received a bogus message:
		_LOG(MSG_WARNING, "AVI: Unable to draw frame to memory. Pausing movie");
        m_Paused = true;
        return false;
	}

    // Lock and copy

	BlitToTexture();
    
    return FileEnded;
}
#endif //_PLATFORM_WIN32
